/*
 * jQuery UI OpenLayers @VERSION
 *
 * Copyright (c) 2008 Alexandre Djioev, Roald de Wit
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://docs.jquery.com/UI/OpenLayers
 *
 * Depends:
 *    ui.core.js
 */
(function($) {
  // Code from http://n2.nabble.com/WMS---Problem-with-projection-param-in-WMS-request-td1827837.html
  // Makes OpenLayers use the declared projection in WMS requests to other layers, even when it is not supported by the base map.
  OpenLayers.Layer.WMS.prototype.getFullRequestString =
      function(newParams, altUrl) {
          var projectionCode = this.projection.toString();
          this.params.SRS = (projectionCode == "none") ? null : projectionCode;
          return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(
              this, arguments);
      } 

  
    $.widget("ui.openlayers", {
        plugins: {},

        _init: function(options) {
            var self = this;

            // allow options to be passed into the init function
            // not sure if this would work
            var o = OpenLayers.Util.extend(options || {}, this.options);
   
            //list to maintain selected features
            this.selectedFeatureList = [];

            this.element.addClass("ui-openlayers");
            this.element.data("name", o.name);
            var mapOptions = {};

            // Set maxExtent for map
            if (o.extent && o.extent.max) {
                var maxExtent = new OpenLayers.Bounds.fromString(o.extent.max);
                if (o.options.displayProjection && o.options.projection) {
                    maxExtent = maxExtent.transform(o.options.displayProjection, o.options.projection)
                }
                mapOptions.maxExtent = maxExtent;
            }

            // Merge options with options from config
            mapOptions = OpenLayers.Util.extend(mapOptions, o.options);

            this.olmap = new OpenLayers.Map(this.element[0], mapOptions);

            //create styles
            var styles = {};
            $.each(o.styles, function(name, config) {
                styles[name] = new OpenLayers.Style(config.template, config.options);
                console.info("Style", config.template, config.options, styles[name]);
            });
             console.info("Styles", styles);

            //create layers
            var layers = [];
            var selectableLayers = [];
            $.each(o.layers, function() {
                //ading layer styles
                if (this.stylemap) {
                    var intentList = {};
                    $.each(this.stylemap, function(intent, style) {
                        intentList[intent] = styles[style];
                    });
                    var styleMap = new OpenLayers.StyleMap(intentList);
                    console.info("StyleMap", styleMap);
                    this.options["styleMap"] = styleMap;
                }

                var layer;
                console.info ('type', this.type);
                switch (this.type) {
                    case OpenLayers.Layer.WFS:
                    case OpenLayers.Layer.WMS:
                        // Raster layer (WMS, etc)
                        layer = new this.type( this.name,
                                               this.url,
                                               this.params,
                                               this.options);
                        break;
                    case OpenLayers.Layer.Vector:
                        layer = new this.type( this.name,
                                               this.options);
                        break;
                    default:
                        // Google type layers 
                        layer = new this.type( this.name,
                                               this.options);
                }

                // register layer events if defined in the config
                for (var e in this.events) {
                   layer.events.register(e, layer, this.events[e]);
                }
                  
                // adding controls that belong to a layer
                $.each(this.controls || {}, function(name, data) {
                    var control = new OpenLayers.Control[name](layer, data.options);
                    self.olmap.addControl(control);
                    control.activate();
                });
          
                // Add layer to list of selectable layers
                if (this.selectable) {
                    layer.events.register("featureselected", self, function(e) {self.onFeatureSelect.apply(self, [e.feature])});
                    layer.events.register("featureunselected", self, function(e) {self.onFeatureUnselect.apply(self, [e.feature])});
                    selectableLayers.push(layer);
                }

                // Add layer to the stack
                layers.push(layer);
            });

            // Actually add the layers to the map
            this.olmap.addLayers(layers);

            //load map controls
            console.info(o.controls);
            $.each(o.controls || {}, function(name, options) {
                var control = new OpenLayers.Control[name](options);
                self.olmap.addControl(control);
            });

            // highlightControl (needs to be defined before selectFeatureControl!)
            var highlightFeatureControl = new OpenLayers.Control.SelectFeature(selectableLayers,{
                    hover: true, 
                    highlightOnly: true, // feature will not be selected, only highlighted
                });
            self.olmap.addControl(highlightFeatureControl);
            highlightFeatureControl.activate();   

            // selectFeatureControl
            var selectFeatureControl = new OpenLayers.Control.SelectFeature(selectableLayers);
            self.olmap.addControl(selectFeatureControl);
            selectFeatureControl.activate();   

            // Attach selectFeatureControl to layer
            $.each(selectableLayers, 
                function() {
                    this.selectFeatureControl = selectFeatureControl;
                });

            //console.info('map controls', selectFeatureControl); 


            //set extent
            if (o.extent && o.extent.initial) {
                var bounds = new OpenLayers.Bounds.fromString(o.extent.initial);
                if (this.olmap.displayProjection && this.olmap.getProjectionObject()) {
                    console.info(this.olmap.displayProjection, this.olmap.getProjectionObject());
                    bounds = bounds.transform(this.olmap.displayProjection, this.olmap.getProjectionObject())
                }
                this.olmap.zoomToExtent(bounds);   
                console.info(bounds, this.olmap.getExtent());
            } else {
                this.olmap.zoomToMaxExtent();
            }
        },
      
        //TODO
        destroy: function() {
            this.element.unbind();
            this.element.removeClass('ui-openlayers').empty();
        },
        // TODO
        enable: function() {
            this.element.removeClass('ui-openlayers-disabled');
            this.disabled = false;
        },
        // TODO
        disable: function() {
            this.element.addClass('ui-openlayers-disabled');
            this.disabled = true;
        },

        //Searches for a feature and returns one
        //
        //options.layer - layer name
        //options.path - attribute path
        //options.value - value of the attribute
        features: function(options) {
            var featureList = [];
            var layer = this.olmap.getLayersByName(options.layer)[0]; //TODO (adjioev) loop through all layers?
            console.info(this.olmap);
            if (layer) {
                $.each(layer.features, function() {
                    if (this.attributes[options.attr] == options.value) {
                        featureList.push(this);
                    }
                });
                return featureList;
            }
            return false;
        },

        getFeaturesByAttribute: function(key, value, layer) {
            console.info("getFeaturesByAttribute", key, value);
            var features = [];

            // layer is provided and is object. Use that one
            if (layer && typeof(layer) == "object") {
                layers = [layer];
            } 
            // layer is provided and is string, find the layer object
            else if (layer && typeof(layer) == "string") {
                layers = [this.olmap.getLayersByName(layer)[0]];
            // otherwise, search through all layers as a last resort
            }
            else {
                layers = this.olmap.layers;
            }

            //console.info("layers", layers);

            // loop through list of layers
            $.each(layers, function() {
                // If layer is a vector layer (has features, loop through them)
                if (this.features) {
                    var l = this;
                    //console.info("layer", l);
                    // looping is done here
                    $.each(l.features, function() {
                        //console.info("feature", this.attributes[key], value);
                        if (this.attributes[key] == value) {
                            features.push(this);
                        }
                    });
                }
            });
            return features;
        },

        onFeatureOver: function(feature) {
            console.info("Feature Over", feature);
            //document.body.style.cursor = "pointer";
            //feature.layer.drawFeature(feature, "select");
        },

        onFeatureOut: function(feature) {
            console.info("Feature Out", feature);
            //document.body.style.cursor = "default";
            //feature.layer.drawFeature(feature, "default");
        },

        onFeatureSelect: function(feature) {
            console.info("Feature Selected", feature);
            this.addPopup(feature); // removes any existing popups
            this.element.trigger("selected", feature);
        },

        onFeatureUnselect: function(feature) {
            console.info("Feature Unselected", feature);
            this.removePopups();// hack to remove popup (won't go away on close button)
        },

        addPopup: function(feature) {
            this.removePopups(); // removes any existing popups
            var template = feature.layer.options.popup.template;
            var content = $.simpleTemplate(template, feature.attributes);
            //console.info("content", feature.layer.options, content);

            var popup = new OpenLayers.Popup.FramedCloud("Popup", 
                            feature.geometry.getBounds().getCenterLonLat(),
                            null, // content size
                            content,
                            null, // anchor
                            true, // close box 
                            function() { // close box callback
                                feature.layer.selectFeatureControl.unselect(feature)
                            }
                        ); 
            popup.autoSize = true;
            feature.popup = popup;
            this.olmap.addPopup(popup);
        },

        /* removePopups:
         * If no arguments provided, this will remove all
         * existing popups. Otherwise it will only remove
         * the popup passed into the function.
         */
        removePopups: function(popup) {
            var popupList = [];
            if (popup != null) {
                popupList.push(popup);
            } else {
                for (var p in this.olmap.popups) {
                    popupList.push(this.olmap.popups[p]);
                }
            }

            for (var p in popupList) {
                var popup = popupList[p];
                this.olmap.removePopup(popup);
                if (popup && typeof(popup.destroy == "function")) {
                    popup.destroy();
                }
                popup = null;
            }
        },

        setPopupContent: function(popup, content){
            console.info("setPopupContent", popup, content);
            popup.setContentHTML(content);
        },

        //pan map to the given LonLat string
        pan: function(lonlat) {
            var coords = OpenLayers.LonLat.fromString(lonlat);
            this.olmap.panTo(coords);
        },

        //returns map object
        map: function() {
            return this.olmap;     
        },

        setLayer: function() {
            console.log("setting a layer");
        }

  });

    $.ui.openlayers.getter = "map, features";
    $.ui.openlayers.defaults = {
        controls: ["LayerSwitcher", "ScaleLine", "NavigationHistory"]
    };


})(jQuery);
